package org.rency.ues.domainservice;

import java.io.UnsupportedEncodingException;
import java.util.Date;

import javax.annotation.Resource;
import javax.jws.WebService;

import org.rency.common.utils.enums.CharsetEnum;
import org.rency.common.utils.security.SHAUtils;
import org.rency.common.utils.tool.DateUtils;
import org.rency.ues.common.enums.EncryptType;
import org.rency.ues.dal.repository.DataHashRepository;
import org.rency.ues.dal.repository.EncryptDataRepository;
import org.rency.ues.domainservice.crypto.support.CryptoFactory;
import org.rency.ues.domainservice.manage.CertificationManager;
import org.rency.ues.domainservice.support.Certification;
import org.rency.ues.domainservice.support.DataHash;
import org.rency.ues.domainservice.support.EncryptData;
import org.rency.ues.domainservice.support.UesConverter;
import org.rency.ues.facade.UesFacade;
import org.rency.ues.request.EncryptRequest;
import org.rency.ues.request.UesRequest;
import org.rency.ues.response.UesResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

@WebService(endpointInterface = "org.rency.ues.facade.UesFacade", targetNamespace = "http://facade.ues.rency.org")
public class DefaultUesFacadeImpl implements UesFacade{
	
	private static final Logger logger = LoggerFactory.getLogger(DefaultUesFacadeImpl.class);
	
	public static final String MSG_TICKET_NOT_EXIST = "ticket not exist in ues.";

	@Resource(name="certificationManager")
	private CertificationManager                 certificationManager;
	
	@Resource(name="encryptDataRepository")
	private EncryptDataRepository               encryptDataRepository;
	
	@Resource(name="dataHashRepository")
	private DataHashRepository                   dataHashRepository;
	
	private Object lock = new Object();
	
	@Override
	public UesResponse getCertBase(EncryptRequest request) {
		Assert.notNull(request, "获取证书请求不能为空");
		Assert.notNull(request.getSerialNo(), "获取证书请求序列号不能为空");
		String certBase = this.getCertification(request.getSerialNo(),request.getCertVersion()).getCertBase();
		return UesResponse.success(certBase, null);
	}
	
	private Certification getCertification(String serialNo,long version){
		Certification certification = certificationManager.getCertification(serialNo, version);
		Assert.notNull(certification, "Invalid certification serial number.[" + serialNo + "]");
		return certification;
	}

	@Override
	public UesResponse saveData(EncryptRequest request) {
		logger.info("save encrypt data : [{}].",request);
		Assert.notNull(request, "save encrypt data request is empty.");
		Assert.notNull(request.getEncryptKey(), "save encrypt data, and encrypt key is empty.");
		Assert.notNull(request.getEncryptData(), "save encrypt data, and encrypt data is empty.");
		Assert.notNull(request.getEncryptType(), "save encrypt data, and encrypt type is empty.");
		Assert.notNull(request.getSerialNo(), "save encrypt data serialNo is empty.");
		Certification certification = certificationManager.getCertification(request.getSerialNo(), request.getCertVersion());
		Assert.notNull(certification, "Invalid certification serial number.[" + request.getSerialNo() + "], and cert version is ["+request.getCertVersion()+"]");
		
		String plainData = null;
		try {
			plainData = this.getPlainData(request.getEncryptType(), request.getEncryptKey(), request.getEncryptData(), certification);
		} catch (UnsupportedEncodingException e) {
			logger.error("save encrypt data error.",e);
			return UesResponse.fail(null, e.getMessage());
		}
		String dataHashString = SHAUtils.SHA256(plainData+request.getEncryptType().getCode()+request.getSerialNo()+request.getCertVersion());
		synchronized(this.lock){
			DataHash orgiDataHash = UesConverter.convert(dataHashRepository.getByHash(dataHashString));
			if(orgiDataHash != null){
				logger.info("encrypt data info exists, and return ticket[{}].",orgiDataHash.getTicket());
				return UesResponse.success(plainData,orgiDataHash.getTicket());
			}
			EncryptData encryptData = this.build(request,certification.getVersion());
			if(encryptDataRepository.saveWithRela(UesConverter.convert(encryptData),dataHashString)){
				logger.info("encrypt data success, and ticket is [{}].",encryptData.getTicket());
				return UesResponse.success(plainData,encryptData.getTicket());
			}
		}
		logger.info("encrypt data fail.");
		return UesResponse.fail(plainData,"encrypt data fail!");
	}
	
	private String getPlainData(EncryptType encryptType,String encryptKey,String encryptData,Certification certification) throws UnsupportedEncodingException{
		byte[] resultKey = CryptoFactory.createDecryptor(encryptType).decrypt(encryptKey, new Object[] { certification });
		String keyTxt = new String(resultKey, CharsetEnum.UTF_8.getCode());
		byte[] resultData = CryptoFactory.createDecryptor(EncryptType.DES).decrypt(encryptData, new Object[] { keyTxt });
		return new String(resultData);
	}

	@Override
	public UesResponse getByTicket(UesRequest request) {
		logger.info("get encrypt data by ticket : [{}].",request);
		Assert.notNull(request, "get encrypt data request is empty.");
		Assert.notNull(request.getTicket(), "get encrypt data ticket is empty.");
		Assert.notNull(request.getEncryptType(), "get encrypt data type is empty.");
		Assert.notNull(request.getSerialNo(), "get encrypt data serialNo is empty.");
		EncryptData encryptData = UesConverter.convert(encryptDataRepository.getByTicket(request.getTicket()));
		if(encryptData == null){
			logger.info("ticket [{}] dose not exists in ues.",request.getTicket());
			return UesResponse.fail(request.getTicket(), MSG_TICKET_NOT_EXIST);
		}
		Certification orgiCertification = certificationManager.getCertification(request.getSerialNo(),encryptData.getVersion());
		Assert.notNull(orgiCertification, "Invalid certification version.[" + encryptData.getVersion() + "]");
		String plainData = null;
		try {
			plainData = this.getPlainData(encryptData.getEncryptType(), encryptData.getEncryptKey(), encryptData.getEncryptData(), orgiCertification);
		} catch (UnsupportedEncodingException e) {
			logger.error("get encrypt data by ticket error.",e);
			return UesResponse.fail(null, e.getMessage());
		}
		logger.info("get plain data by ticket [{}]=[{}] success.",request.getTicket(),plainData);
		return UesResponse.success(plainData, request.getTicket());
	}

	private EncryptData build(EncryptRequest request,long version){
		EncryptData encryptData = new EncryptData();
		encryptData.setEncryptData(request.getEncryptData());
		encryptData.setEncryptKey(request.getEncryptKey());
		encryptData.setEncryptType(request.getEncryptType());
		Date expireTime = request.getExpireTime() == 0 ? DateUtils.addYear(new Date(),50) : DateUtils.addSeconds(new Date(), request.getExpireTime());
		encryptData.setExpireTime(expireTime);
		encryptData.setTicket(encryptDataRepository.genTicketId());
		encryptData.setVersion(version);
		return encryptData;
	}
}
